﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;

namespace Bonobo.Irc.Server
{
    public sealed class IrcServer : IDisposable
    {
        private readonly Object _syncRoot = new Object();
        private readonly List<IrcClient> _clients = new List<IrcClient>();

        private TcpListener _listener;

        public IrcServer()
            : this(IrcProtocol.DefaultTcpPort)
        {
        }

        public IrcServer(Int32 port)
            : this(new IPEndPoint(IPAddress.Any, port))
        {
        }

        public IrcServer(IPEndPoint endpoint)
        {
            _listener = new TcpListener(endpoint);
        }

        public void Start()
        {
            _listener.Start();
            _listener.BeginAcceptTcpClient(OnClientAccepted, null);
        }

        public void Stop()
        {
            if (_listener != null)
            {
                _listener.Stop();
                _listener = null;
            }

            lock (_syncRoot)
            {
                foreach (var client in _clients)
                {
                    client.StateChanged -= OnClientStateChanged;
                    client.Dispose();
                }

                _clients.Clear();
            }
        }

        private void OnClientAccepted(IAsyncResult ar)
        {
            IrcClient ircClient = null;
            TcpClient tcpClient = null;

            try
            {
                tcpClient = _listener.EndAcceptTcpClient(ar);

                ircClient = new IrcClient(this, tcpClient);
                ircClient.StateChanged += OnClientStateChanged;

                lock (_syncRoot)
                {
                    _clients.Add(ircClient);
                }
            }
            catch (Exception exc)
            {
                if (ircClient != null)
                {
                    ircClient.Dispose();
                }

                if (tcpClient != null)
                {
                    tcpClient.Close();
                }

                Trace.TraceError("OnClientAccepted: {0}", exc);
            }
            finally
            {
                _listener.BeginAcceptTcpClient(OnClientAccepted, null);
            }
        }

        private void OnClientStateChanged(Object sender, IrcConnectionStateEventArgs e)
        {
            if (e.State == IrcConnectionState.Closed)
            {
                lock (_syncRoot)
                {
                    _clients.Remove((IrcClient)(sender));
                }
            }
        }

        void IDisposable.Dispose()
        {
            Stop();
        }
    }
}
